package selector

import (
	"context"
	sql "database/sql"
	"reflect"
	errors2 "server/framework/orm/errors"
	"server/framework/orm/middleware"
	"server/framework/orm/predicate"
	"server/framework/orm/session/db"
	value2 "server/framework/orm/session/value"
	sql2 "server/framework/orm/sql"
	"server/framework/orm/sql/common"
	"server/framework/orm/sql/querior/selector/order_by"
	"server/framework/orm/utils"
)

/*
*
Selector[T]: select []selectable from tableReference where []predicate; 支持group by、having、order by、limit、offset;

selectable支持column、aggregate(聚合函数)、raw(万金油)

tableReference支持用户自定义输入、反射|unsafe取T（结构体名）、子查询语句、join语句

predicate支持
 1. age < 18
 2. age < 18 and id > 5
 3. Not id < 5
*/

type Selector[T any] struct {
	selects []predicate.Selectable
	table   predicate.TableReference
	wheres  []predicate.Predicate

	groupBys []predicate.Column
	having   []predicate.Predicate
	orderBys []order_by.OrderBy
	limit    int
	offset   int

	middlewares []middleware.Middleware

	sess db.Session
	common.SentenceBuilder
}

func (s *Selector[T]) buildSelect() error {
	s.Builder.WriteString("select ")
	if len(s.selects) > 0 {
		for i, item := range s.selects {
			// select 部分能够定义别名，不能使用别名；别名将在其他部分比如order by中使用
			err := s.BuildExpression(item.(predicate.Expression), sql2.UseAlias{Build: true, Use: false})
			if err != nil {
				return err
			}
			if i < len(s.selects)-1 {
				s.Builder.WriteString(",")
			}
		}
	} else {
		s.Builder.WriteString("*")
	}

	return nil
}

func (s *Selector[T]) buildWhere() error {
	if len(s.wheres) > 0 {
		s.Builder.WriteString(" where ")
		for i, item := range s.wheres {
			// where处不能定义、使用别名
			err := s.BuildPredicate(item, sql2.UseAlias{Build: false, Use: false})
			if err != nil {
				return err
			}

			if i != len(s.wheres)-1 {
				s.Builder.WriteString(" " + predicate.OpAnd.String() + " ")
			}
		}
	}

	return nil
}

func (s *Selector[T]) buildFrom() error {
	s.Builder.WriteString(" from ")
	alias := sql2.UseAlias{Build: true, Use: false}
	switch entity := s.table.(type) {
	case nil:
		s.Builder.WriteString(s.GenerateField(s.Model.TableName))
	case predicate.Table:
		s.BuildTable(entity, alias)
	case predicate.Join:
		err := s.BuildJoin(entity)
		if err != nil {
			return err
		}
	case predicate.SubQuery:
		err := s.BuildSubQuery(entity, alias)
		if err != nil {
			return err
		}
	default:
		return errors2.ErrUnknownTable(entity)
	}
	return nil
}

func (s *Selector[T]) buildGroupBy() error {
	if len(s.groupBys) > 0 {
		s.Builder.WriteString(" group by ")
		for i, column := range s.groupBys {
			// group by处不能定义别名，可以使用别名
			err := s.BuildColumn(column, sql2.UseAlias{Build: false, Use: true})
			if err != nil {
				return err
			}
			if i < len(s.groupBys)-1 {
				s.Builder.WriteString(",")
			}
		}
	}
	return nil
}
func (s *Selector[T]) buildHaving() error {
	if len(s.having) > 0 {
		if len(s.groupBys) == 0 {
			return errors2.ErrNoGroupBy
		}

		s.Builder.WriteString(" having ")
		for i, p := range s.having {
			err := s.BuildPredicate(p, sql2.UseAlias{Build: false, Use: true})
			if err != nil {
				return err
			}
			if i < len(s.having)-1 {
				s.Builder.WriteString(" " + predicate.OpAnd.String() + " ")
			}
		}
	}
	return nil
}
func (s *Selector[T]) buildOrderBy() error {
	if len(s.orderBys) > 0 {
		s.Builder.WriteString(" order by ")
		for i, item := range s.orderBys {
			err := s.BuildColumn(item.Column, sql2.UseAlias{Build: false, Use: true})
			if err != nil {
				return err
			}
			s.Builder.WriteString(" " + item.Order.String())
			if i < len(s.orderBys)-1 {
				s.Builder.WriteString(",")
			}
		}
	}
	return nil
}

func (s *Selector[T]) buildLimit() {
	if s.limit > 0 {
		s.Builder.WriteString(" limit ?")
		s.Args = append(s.Args, s.limit)
	}

}
func (s *Selector[T]) buildOffset() {
	if s.offset > 0 {
		s.Builder.WriteString(" offset ?")
		s.Args = append(s.Args, s.offset)
	}
}

func (s *Selector[T]) Build() ([]*sql2.Query, error) {
	if s.Model == nil {
		var t *T
		s.Model = s.R.Get(t)
	}

	err := s.buildSelect()
	if err != nil {
		return nil, err
	}

	err = s.buildFrom()
	if err != nil {
		return nil, err
	}

	err = s.buildWhere()
	if err != nil {
		return nil, err
	}

	err = s.buildGroupBy()
	if err != nil {
		return nil, err
	}

	err = s.buildHaving()
	if err != nil {
		return nil, err
	}

	err = s.buildOrderBy()
	if err != nil {
		return nil, err
	}

	// limit必须在offset前面
	s.buildLimit()
	s.buildOffset()

	s.Builder.WriteString(";")
	return []*sql2.Query{&sql2.Query{
		SQL:  s.Builder.String(),
		Args: s.Args,
	}}, nil
}

func (s *Selector[T]) Get(ctx context.Context, val interface{}) *sql2.QueryResult {
	queryS, err := s.Build()
	query := queryS[0] // 为了兼容分库分表的Build() []*Query
	if err != nil {
		return &sql2.QueryResult{Err: err}
	}

	var root middleware.Handler = func(c context.Context, qc *sql2.QueryContext) *sql2.QueryResult {
		var rows *sql.Rows
		rows, err = s.sess.QueryContext(ctx, query.SQL, query.Args...)
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}

		defer rows.Close()
		if !rows.Next() {
			return &sql2.QueryResult{Err: errors2.ErrNoRows}
		}

		var value value2.Value

		model := s.R.Get(val)
		value, err = s.ValueCreator(val, model)
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}
		err = value.SetColumns(rows, s.Dialect.Quote())
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}
		return &sql2.QueryResult{
			Res: val,
		}
	}

	for _, m := range s.middlewares {
		root = m(root)
	}
	return root(ctx, &sql2.QueryContext{
		Type:    "Selector",
		Builder: s,
		Model:   s.Model,
	})
}

func (s *Selector[T]) GetMutli(ctx context.Context, vals interface{}) *sql2.QueryResult {
	elem, err := utils.ValidateValsType(vals)
	if err != nil {
		return &sql2.QueryResult{Err: err}
	}

	var root middleware.Handler = func(c context.Context, qc *sql2.QueryContext) *sql2.QueryResult {
		var queryS []*sql2.Query
		queryS, err = s.Build()
		query := queryS[0] // 为了兼容分库分表的Build() []*Query
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}
		var rows *sql.Rows
		rows, err = s.sess.QueryContext(ctx, query.SQL, query.Args...)
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}
		defer rows.Close()

		valInstance := reflect.New(elem).Interface() // 取值
		model := s.R.Get(valInstance)

		var r *value2.ReflectValues
		// model不能在里面生成,需要外面传入原因: 使用s.R中的缓存数据
		r, err = value2.NewReflectValues(vals, model, value2.WithValueCreator(s.ValueCreator))
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}
		err = r.SetValues(rows, s.Dialect.Quote())
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}

		return &sql2.QueryResult{
			Res: vals,
		}
	}

	return root(ctx, &sql2.QueryContext{
		Type:    "Selector",
		Builder: s,
		Model:   s.Model,
	})
}

// Select 支持Column、聚合函数、raw(自定义输入)
func (s *Selector[T]) Select(selectable ...predicate.Selectable) *Selector[T] {
	s.selects = append(s.selects, selectable...)
	return s
}

// From 支持表名、子查询、聚合查询
func (s *Selector[T]) From(table predicate.TableReference) *Selector[T] {
	s.table = table
	return s
}

func (s *Selector[T]) Where(where ...predicate.Predicate) *Selector[T] {
	s.wheres = append(s.wheres, where...)
	return s
}

func (s *Selector[T]) GroupBy(columns ...predicate.Column) *Selector[T] {
	s.groupBys = append(s.groupBys, columns...)
	return s
}

func (s *Selector[T]) Having(predicates ...predicate.Predicate) *Selector[T] {
	s.having = append(s.having, predicates...)
	return s
}

func (s *Selector[T]) OrderBy(orders ...order_by.OrderBy) *Selector[T] {
	s.orderBys = append(s.orderBys, orders...)
	return s
}

func (s *Selector[T]) Limit(limit int) *Selector[T] {
	s.limit = limit
	return s
}

func (s *Selector[T]) Offset(offset int) *Selector[T] {
	s.offset = offset
	return s
}

func (s *Selector[T]) AsSubQuery() *predicate.SubQuery {
	var t *T
	entity := predicate.TableOf(t)
	return &predicate.SubQuery{
		QueryBuilder: s,
		Entity:       entity,
	}
}

func NewSelector[T any](sess db.Session, middlewares ...middleware.Middleware) *Selector[T] {
	return &Selector[T]{
		sess:        sess,
		middlewares: middlewares,
		SentenceBuilder: common.SentenceBuilder{
			Core: sess.GetCore(),
		},
	}
}

var _ sql2.Querior[any] = (*Selector[any])(nil)
